package org.andengine.opengl.texture.atlas;

import org.andengine.opengl.texture.ITextureStateListener;
import org.andengine.opengl.texture.PixelFormat;
import org.andengine.opengl.texture.Texture;
import org.andengine.opengl.texture.TextureManager;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.source.ITextureAtlasSource;

import java.util.ArrayList;

/**
 * (c) 2010 Nicolas Gramlich
 * (c) 2011 Zynga Inc.
 *
 * @author Nicolas Gramlich
 * @since 14:22:55 - 14.07.2011
 */
public abstract class TextureAtlas<T extends ITextureAtlasSource> extends Texture implements ITextureAtlas<T> {
    // ===========================================================
    // Constants
    // ===========================================================

    // ===========================================================
    // Fields
    // ===========================================================

    protected final int mWidth;
    protected final int mHeight;

    protected final ArrayList<T> mTextureAtlasSources = new ArrayList<T>();

    // ===========================================================
    // Constructors
    // ===========================================================

    public TextureAtlas(final TextureManager pTextureManager, final int pWidth, final int pHeight, final PixelFormat pPixelFormat, final TextureOptions pTextureOptions, final ITextureAtlasStateListener<T> pTextureAtlasStateListener) {
        super(pTextureManager, pPixelFormat, pTextureOptions, pTextureAtlasStateListener);

        this.mWidth = pWidth;
        this.mHeight = pHeight;
    }

    // ===========================================================
    // Getter & Setter
    // ===========================================================

    @Override
    public int getWidth() {
        return this.mWidth;
    }

    @Override
    public int getHeight() {
        return this.mHeight;
    }

    @Deprecated
    @Override
    public boolean hasTextureStateListener() {
        return super.hasTextureStateListener();
    }

    @Override
    public boolean hasTextureAtlasStateListener() {
        return super.hasTextureStateListener();
    }

    /**
     * @see {@link ITextureAtlas#setTextureStateListener(ITextureAtlasStateListener)}
     */
    @Deprecated
    @Override
    public ITextureAtlasStateListener<T> getTextureStateListener() {
        return this.getTextureAtlasStateListener();
    }

    @SuppressWarnings("unchecked")
    @Override
    public ITextureAtlasStateListener<T> getTextureAtlasStateListener() {
        return (ITextureAtlasStateListener<T>) super.getTextureStateListener();
    }

    /**
     * @see {@link ITextureAtlas#setTextureStateListener(ITextureAtlasStateListener)}
     */
    @Deprecated
    @Override
    public void setTextureStateListener(final ITextureStateListener pTextureStateListener) {
        super.setTextureStateListener(pTextureStateListener);
    }

    @Override
    public void setTextureAtlasStateListener(final ITextureAtlasStateListener<T> pTextureAtlasStateListener) {
        super.setTextureStateListener(pTextureAtlasStateListener);
    }

    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    @Override
    public void addTextureAtlasSource(final T pTextureAtlasSource, final int pTextureX, final int pTextureY) throws IllegalArgumentException {
        this.checkTextureAtlasSourcePosition(pTextureAtlasSource, pTextureX, pTextureY);
        pTextureAtlasSource.setTextureX(pTextureX);
        pTextureAtlasSource.setTextureY(pTextureY);
        this.mTextureAtlasSources.add(pTextureAtlasSource);
        this.mUpdateOnHardwareNeeded = true;
    }

    @Override
    public void addTextureAtlasSource(final T pTextureAtlasSource, final int pTextureX, final int pTextureY, final int pTextureAtlasSourcePadding) throws IllegalArgumentException {
        this.addTextureAtlasSource(pTextureAtlasSource, pTextureX, pTextureY);

        if (pTextureAtlasSourcePadding > 0) {
            /* Left padding. */
            if (pTextureX >= pTextureAtlasSourcePadding) {
                this.addEmptyTextureAtlasSource(pTextureX - pTextureAtlasSourcePadding, pTextureY, pTextureAtlasSourcePadding, pTextureAtlasSource.getTextureHeight());
            }

			/* Top padding. */
            if (pTextureY >= pTextureAtlasSourcePadding) {
                this.addEmptyTextureAtlasSource(pTextureX, pTextureY - pTextureAtlasSourcePadding, pTextureAtlasSource.getTextureWidth(), pTextureAtlasSourcePadding);
            }

			/* Right padding. */
            if (pTextureX + pTextureAtlasSource.getTextureWidth() - 1 + pTextureAtlasSourcePadding <= this.getWidth()) {
                this.addEmptyTextureAtlasSource(pTextureX + pTextureAtlasSource.getTextureWidth(), pTextureY, pTextureAtlasSourcePadding, pTextureAtlasSource.getTextureHeight());
            }

			/* Bottom padding. */
            if (pTextureY + pTextureAtlasSource.getTextureHeight() - 1 + pTextureAtlasSourcePadding <= this.getHeight()) {
                this.addEmptyTextureAtlasSource(pTextureX, pTextureY + pTextureAtlasSource.getTextureHeight(), pTextureAtlasSource.getTextureWidth(), pTextureAtlasSourcePadding);
            }
        }
    }

    @Override
    public void removeTextureAtlasSource(final T pTextureAtlasSource, final int pTextureX, final int pTextureY) {
        final ArrayList<T> textureSources = this.mTextureAtlasSources;
        for (int i = textureSources.size() - 1; i >= 0; i--) {
            final T textureSource = textureSources.get(i);
            if (textureSource == pTextureAtlasSource && textureSource.getTextureX() == pTextureX && textureSource.getTextureY() == pTextureY) {
                textureSources.remove(i);
                this.mUpdateOnHardwareNeeded = true;
                return;
            }
        }
    }

    @Override
    public void clearTextureAtlasSources() {
        this.mTextureAtlasSources.clear();
        this.mUpdateOnHardwareNeeded = true;
    }

    // ===========================================================
    // Methods
    // ===========================================================

    private void checkTextureAtlasSourcePosition(final T pTextureAtlasSource, final int pTextureX, final int pTextureY) throws IllegalArgumentException {
        if (pTextureX < 0) {
            throw new IllegalArgumentException("Illegal negative pTextureX supplied: '" + pTextureX + "'");
        } else if (pTextureY < 0) {
            throw new IllegalArgumentException("Illegal negative pTextureY supplied: '" + pTextureY + "'");
        } else if (pTextureX + pTextureAtlasSource.getTextureWidth() > this.getWidth() || pTextureY + pTextureAtlasSource.getTextureHeight() > this.getHeight()) {
            throw new IllegalArgumentException("Supplied pTextureAtlasSource must not exceed bounds of Texture.");
        }
    }

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================
}
